package com.crc.luojian.factory.impl;

import com.crc.luojian.annotation.Autowired;
import com.crc.luojian.annotation.Service;
import com.crc.luojian.beans.model.BeanDefinition;
import com.crc.luojian.factory.BeanFactory;
import com.crc.luojian.factory.BeanPostProcessor;
import com.crc.luojian.util.BeanUtils;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * @program: luojian-springframework
 * @description:  默认的BeanFactory
 * @author: luojian58
 * @create: 2021-06-10 10:50
 */
public class DefaultBeanFactory extends SingletonBeanRegistery  implements BeanFactory {

    private boolean allowCircularReferences = true;

    private Map<String,BeanDefinition> beanDefinitionMap = new ConcurrentHashMap();
    /** BeanPostProcessors to apply in createBean. */
    private final List<BeanPostProcessor> beanPostProcessors = new CopyOnWriteArrayList<>();

    /**
     * @Author luojian
     * @Description  将beanDefinition 放入map
     * @Date  2021/6/10 10:55
     * @Param [beanName, beanDefinition]
     * @return void
     **/
    public void registerBeanDefinition(String beanName,BeanDefinition beanDefinition){
        beanDefinitionMap.put(beanName,beanDefinition);
    }

    @Override
    public Object getBean(String name) {
        return doGetBean(name);
    }

    /**
     * @Author luojian
     * @Description  从缓存中获取bean ，没有获取到通过beanfactory 创建返回
     * @Date  2021/6/10 13:40
     * @Param [name]
     * @return java.lang.Object
     **/
    private Object doGetBean(String name){
        Object bean ;
        Object sharedInstance = getSingleton(name,true);
        if(sharedInstance != null){
            bean = sharedInstance;
        }else{
            BeanDefinition beanDefinition = beanDefinitionMap.get(name);
            if(beanDefinition == null){
                throw new RuntimeException("name: "+name+" BeanDefinition not found");
            }else{
               bean = getSingleton(name,()->{
                   return  doCreateBean(name,beanDefinition); 
                });
            }
        }
        return bean;
    }

    /**
     * @Author luojian
     * @Description 创建bean 并解决循环依赖的问题
     * @Date  2021/6/10 14:30
     * @Param [name, beanDefinition]
     * @return java.lang.Object
     **/
    private Object doCreateBean(String name, BeanDefinition beanDefinition) {
        Object bean = createInstance(name, beanDefinition);
        boolean earlySingletonExposure = allowCircularReferences && isSingletonCurrentlyInCreation(name);
        //如果允许提前引用 将为设置属性的对象工厂 放入三级缓存
        if (earlySingletonExposure) {
            addSingletonFactory(name, () -> getEarlyBeanReference(name,bean));
        }
        //设置属性
        Object exposedObject = bean;
        populateBean(name, beanDefinition, bean);
        exposedObject = initializeBean(name, exposedObject);
        if (earlySingletonExposure) {
            Object earlySingletonReference = getSingleton(name, false);
            if (earlySingletonReference != null) {
                if (exposedObject == bean) {
                    exposedObject = earlySingletonReference;
                }
            }
        }
        return  exposedObject;

    }

    protected Object getEarlyBeanReference(String beanName, Object bean) {

        return bean;
    }

    //暂不实现
    private Object initializeBean(String beanName, Object exposedObject) {
        exposedObject = applyBeanPostProcessorsBeforeInitialization(exposedObject, beanName);
        return applyBeanPostProcessorsAfterInitialization(exposedObject, beanName);
    }

    //初始化前置执行器
    private Object applyBeanPostProcessorsBeforeInitialization(Object exposedObject, String beanName) {
        if(!Objects.isNull(beanPostProcessors) && beanPostProcessors.size()>0){
            for (BeanPostProcessor beanPostProcessor: beanPostProcessors) {
                    exposedObject = beanPostProcessor.postProcessBeforeInitialization(exposedObject, beanName);
            }
        }
        return exposedObject;
    }
    //初始化后置执行器
    private Object applyBeanPostProcessorsAfterInitialization(Object exposedObject, String beanName) {
        if(!Objects.isNull(beanPostProcessors) && beanPostProcessors.size()>0){
            for (BeanPostProcessor beanPostProcessor: beanPostProcessors) {
                    exposedObject = beanPostProcessor.postProcessAfterInitialization(exposedObject, beanName);
            }
        }
        return exposedObject;
    }


    /**
     * @Author luojian
     * @Description 初始化属性
     * @Date  2021/6/10 14:17
     * @Param [name, beanDefinition, bean]
     * @return void
     **/
    private void populateBean(String name, BeanDefinition beanDefinition, Object bean) {
        try {
            Field[] declaredFields = beanDefinition.getBenaClass().getDeclaredFields();
            for (Field field : declaredFields) {
                if (field.getAnnotation(Autowired.class) == null) {
                    continue;
                }
                if (!containsBean(field.getName())) {
                    throw new RuntimeException(" field '" + field.getClass().getName() + "' can not find");
                }

                field.setAccessible(true);
                field.set(bean, getBean(field.getName()));
                //在初始化Bservice 中的aservice 取的是一ObjectFactory<?> singletonFactory= singletonFactories.get(beanName); 的未初始化完全的对象
            }
        }catch (Exception e){
            throw new RuntimeException(e);
        }
    }


    private boolean containsBeanDefinition(String name) {
        return beanDefinitionMap.containsKey(name);
    }
    /**
     * @Author luojian
     * @Description  通过beanDefinition 获取class， 通过class 获取无从构造函数设置访问权限 newInstance()
     * @Date  2021/6/10 13:46
     * @Param [beanName, beanDefinition]
     * @return java.lang.Object
     **/
    private Object createInstance(String beanName,BeanDefinition beanDefinition)  {
        Class<?> beanClass = beanDefinition.getBenaClass();
        if(beanClass.isInterface()){
            throw  new RuntimeException("Specified class '" + beanName + "' is an interface");
        }
        try {
            Constructor constructor = beanClass.getDeclaredConstructor((Class<?>[]) null);
            return BeanUtils.instantiateClass(constructor);
        }catch (Exception e){
            throw  new RuntimeException("beanName: '" + beanName + " ,No default constructor found",e);
        }

    }

    @Override
    public <T> T getBean(Class<T> type) {
        return (T)getBean(BeanUtils.lowerFirst(type.getSimpleName()));

    }

    @Override
    public boolean containsBean(String name) {
        return containsSingleton(name) || this.containsBeanDefinition(name);
    }

    public void preInstantiateSingletons() {
        beanDefinitionMap.forEach((beanName,beanDefinition)->{
            getBean(beanName);
        });
    }

    public void loadBeanPostProcessor() {
        beanPostProcessors.add(new AutoProxyCreator());
    }
}
